import time
import tushare as ts
import time
import datetime
from LSTM.variableset import EMVar
from emmongo import em_mongomanager as mongoManager
import pandas as pd
import json
import talib
from emutils import time_utils

em_tick_data_cache = {}

def em_get_tick_data(code=None, date=None,maxkeepCount=50):
    global em_tick_data_cache
    if not code or not date:
        return
    code_key = code + '_' + date
    if len(em_tick_data_cache) > maxkeepCount:
        em_tick_data_cache.clear()
    if code_key in em_tick_data_cache.keys():
        return em_tick_data_cache[code_key]
    else:
        time.sleep(5)
        try:
            data = ts.get_tick_data(code=code, date=date)
        except Exception as e:
            print('%s:%s Tushare Tick Data 获取失败'%(code, date))
            time.sleep(300)
        em_tick_data_cache[code_key] = data
        return data


em_h_data = {}
def em_get_h_data(code=None, index=False, start='2010-01-01', end=datetime.datetime.now().strftime('%Y-%m-%d'),maxKeepCount=50):
    global em_h_data
    if not code :
        return
    code_key = code+'_'+str(index)
    if len(em_h_data) > maxKeepCount:
        em_h_data.clear()
    total_data = None
    if code_key in em_tick_data_cache.keys():
        total_data = em_h_data[code_key]
    else:
        total_data = get_h_data(code=code, index=index,ktype='D',autype='qfq')
        em_h_data[code_key] = total_data
    return total_data[start:end]

init_once = {}
def get_h_data(code=None, index=False, start='2010-01-01', end=datetime.datetime.now().strftime('%Y-%m-%d'),autype='qfq',ktype='D',need_insert_db=True):
    col_name = mongoManager.COL_INDEX_HIS_DATA if index else mongoManager.COL_STK_HIS_DATA
    l_t = mongoManager.db[col_name].find({'$and':[{EMVar.date:{'$lte':start}}, {EMVar.code:code},{EMVar.ktype:ktype},{EMVar.autype:autype}]})
    g_t = mongoManager.db[col_name].find({'$and': [{EMVar.date: {'$gte': end}}, {EMVar.code: code}, {EMVar.ktype: ktype}, {EMVar.autype: autype}]})
    l_datas = mongoManager.db[col_name].find({EMVar.date: {'$gte': start}, EMVar.date: {'$lte': end}, EMVar.code: code, EMVar.ktype: ktype,EMVar.autype: autype})
    new_df = pd.DataFrame()
    # if len(list(l_t))==0 or len(list(g_t))==0:
    if len(list(l_datas))==0 or code not in init_once.keys():
        # 从Tushare中获取
        new_df = ts.get_k_data(code=code, index=index, start=start, end=end, autype=autype,ktype=ktype)
        new_df[EMVar.autype] = autype
        new_df[EMVar.ktype] = ktype
        new_df[EMVar.code] = code
        date_series = new_df[EMVar.date]
        new_df.set_index(date_series, inplace=True)
        # talib_cal(data=new_df)
        init_once[code] = datetime.datetime.now().strftime('%Y-%m-%d')
        pass
    else:
        # 从mongo中获取
        datas = mongoManager.db[col_name].find({EMVar.date:{'$gte':start},EMVar.date:{'$lte':end},EMVar.code:code,EMVar.ktype:ktype,EMVar.autype:autype})
        new_df = pd.DataFrame(data=list(datas))
        date_series = new_df[EMVar.date]
        new_df.set_index(date_series, inplace=True)
        need_insert_db = False
        pass
    if need_insert_db:
        for index, row in new_df.iterrows():
            value = row.to_dict()
            mongoManager.db[col_name].update_one(value, {'$set': {EMVar.date: value[EMVar.date], EMVar.code:value[EMVar.code], EMVar.autype:value[EMVar.autype], EMVar.ktype:value[EMVar.ktype]}}, upsert=True)
    return new_df


# 判断当前时刻是否需要加上最新的价格, 后面可以在盘中试一下测试这个
def need_append_now(last_his_date):
    end = datetime.datetime.now()
    # 由于Tushare中获取历史序列的时候只要到了收盘后第二天的凌晨即可获取前一天，
    # 例如15点到24点的时候，获取历史数据是无法获取到当天的。所以只能用实时的数据去填补这个序列
    is_shoupan = end.hour >= 15 or (end.hour <= 23 and end.minute > 58)
    #  需要在这里判断最后一个是否是周五，是的话不做处理
    df_last_datetime = last_his_date if isinstance(last_his_date, datetime.datetime) else datetime.datetime.strptime(last_his_date, '%Y-%m-%d')
    day_delta = (datetime.datetime.now() - df_last_datetime).days
    append_now = is_shoupan and (day_delta > 0) and (end.weekday() != 5 and end.weekday() != 6)
    print('历史数据最后一天是%s， 当前时间是%s , 调用获取当天历史数据接口:%d'%(last_his_date, end.strftime('%Y-%m-%d'), append_now))
    return append_now


# 获取当天的记录作为历史数据序列
def nowday_h_data(code):
    realtime = ts.get_realtime_quotes(code)[[EMVar.code, EMVar.date, EMVar.open, 'price', EMVar.high, EMVar.low, EMVar.volume]]
    if realtime.shape[0] == 1:
        print('%s 在 %s 最新的历史数据获取成功' % (code, datetime.datetime.now().strftime(EMVar.FORMATE_YMD)))
        today_s = pd.DataFrame(data=[[code, realtime[EMVar.date][0], float(realtime[EMVar.open][0]), float(realtime['price'][0]), float(realtime[EMVar.high][0]), float(realtime[EMVar.low][0]), (int(realtime[EMVar.volume]) / 100)]],
                               columns=EMVar.BASE_COLUMNS)
        return today_s
    else:
        return pd.DataFrame()




def talib_cal(data,indicator=[EMVar.ID_MACD, EMVar.ID_BOLL,EMVar.ID_MA]):
    if not isinstance(data, pd.DataFrame):
        raise Exception('Data should be dataframe')
    if EMVar.ID_MA:
        data[EMVar.MA5] = talib.MA(data[EMVar.close].values, timeperiod=5,matype=0)
        data[EMVar.MA10] = talib.MA(data[EMVar.close].values, timeperiod=10, matype=0)
        data[EMVar.MA20] = talib.MA(data[EMVar.close].values, timeperiod=20, matype=0)
        data[EMVar.MA30] = talib.MA(data[EMVar.close].values, timeperiod=30, matype=0)
        data[EMVar.MA60] = talib.MA(data[EMVar.close].values, timeperiod=60, matype=0)
    if EMVar.ID_MACD in indicator:
        # https: // www.joinquant.com / post / 131
        # talib中是DIF-DEA，其他地方是 2*(DIF-DEA)
        data[EMVar.DIF],data[EMVar.DEA],data[EMVar.MACD] = talib.MACD(data[EMVar.close].values, fastperiod=12, slowperiod=26, signalperiod=9)
    if EMVar.ID_BOLL in indicator:
        # https://www.joinquant.com/post/144
        # 布林线中的压力位与阻力位
        data[EMVar.BOLL_UPPER], data[EMVar.BOLL_MIDDLE], data[EMVar.BOLL_LOWER] = talib.BBANDS(data[EMVar.close].values,timeperiod=20,nbdevup=2,nbdevdn=2,matype=0)



# 最终是要快速获取某一天的情况,可能是若干票

# em_get_h_data(EMVar.CODE_SH, index=True,start='2016-01-01',end='2016-01-01')



# def em_get_tick_data_between(code=None, index=False, action_date=None, start=None, end=None):
#     if not code or not start or not end:
#         raise  Exception('Params Error:Code or start or end null')
#     date_index_list = em_get_h_data(code=code, index=index).index
#     pass

def em_get_tick_data_offset(code=None, date=None, date_index_list=None, pre_offset=0, later_offset=0,time_sleep=0):
    date_l, date_index_offset = em_get_date_offset(date, date_index_list, pre_offset, later_offset)
    if date_l:
        pre_datas = []
        late_datas = []
        if time_sleep!=0:
            time.sleep(time_sleep)
        for i in range(pre_offset, 0, 1):
            # dmi 是 研究买入前若干天
            dmi = date_index_list[date_index_offset-abs(i)]
            dmi_data = em_get_tick_data(code=code, date=dmi)
            pre_datas.append(dmi_data)

        for i in range(1, later_offset+1, 1):
            # dpi 是买入之后的若干天
            dpi = date_index_list[date_index_offset+abs(i)]
            dpi_datas = em_get_tick_data(code=code, date=dpi)
            late_datas.append(dpi_datas)
        print(' curDate: %s，获取日期范围: %s ' % (date, date_l))
        return pre_datas, late_datas


def em_get_date_offset(date=None, date_index_list=None, pre_offset=0, later_offset=0, ignore_later_offset=False):
    if isinstance(date_index_list, list) and date_index_list == None:
        raise Exception('日期序列不能为空')
    if isinstance(date_index_list, pd.Series) and date_index_list.empty:
        raise Exception('日期序列不能为空')
    if not isinstance(date, str):
        import datetime
        date = datetime.datetime.strftime(date, '%Y-%m-%d')
    if not isinstance(date_index_list, list):
        date_index_list = list(date_index_list)
    if date not in date_index_list:
        raise Exception('date 不在 date_index_list 中')

    if pre_offset>0:
        raise Exception('获取之前日期Ticket的偏移值必须<=0')
    if later_offset<0:
        raise Exception('获取后面日期Ticket的偏移值必须>=0')


    date_index_offset = date_index_list.index(date)
    if (date_index_offset - abs(pre_offset) <= 0) :
        return None, 0
    if not ignore_later_offset:
        if (date_index_offset + abs(later_offset) >= len(date_index_list)):
            return None, 0
    date_l = date_index_list[date_index_offset + pre_offset:date_index_offset + abs(later_offset) + 1:1]
    return date_l, date_index_offset


def get_count(price, total_money):
    return int(total_money/(price*100))*100

def insert_stop_trade_record(code, date, is_index=False):
    mongoManager.db[mongoManager.COL_NOT_TRADE_DATA].update_one({EMVar.code:code, EMVar.date:date, 'is_index':is_index},{'$set': {EMVar.code:code, EMVar.date: date}},upsert=True)


def is_stop_trade(code, date):
    return mongoManager.db[mongoManager.COL_NOT_TRADE_DATA].find({EMVar.code:code, EMVar.date:date}).count() == 1



em_min_data_cache = {}
def em_resample_tick_data(code=None, date=None, resample_periods=[1], need_insert_db=True, maxKeepCount=100):
    if not isinstance(date, str):
        raise Exception('date should be string format')
    global em_min_data_cache
    if len(em_min_data_cache) > maxKeepCount:
        em_min_data_cache.clear()
    if not code or not date:
        return
    codekey = code+'_'+date
    if codekey in em_min_data_cache.keys():
        print('从内存缓存中获取%s在%s的1分钟K线'%(code, date))
        return em_min_data_cache[codekey]
    else:
        df = resample_tick_data(code, date, need_insert_db=need_insert_db)
        em_min_data_cache[codekey] = df
        return df


def resample_tick_data(code=None, date=None, resample_periods=[1], need_insert_db=True):
    if not isinstance(date, str):
        raise Exception('date should be string format')

    resample_baser_period = '1min'

    # resample_baser_period = 1
    # if isinstance(resample_periods, list):
    #     resample_baser_period = resample_periods.min()
    #     l = [x + 'min' for x in resample_periods]

    if is_stop_trade(code=code, date=date):
        print('%s获取%sTick数据失败,当天没有数据' % (code, date))
        return pd.DataFrame()
    new_df = mongo_min_tick_data(code, date, kType='1min')
    if (new_df.empty or new_df.shape[0] < 230):
        df = pd.DataFrame()
        try:
             df = em_get_tick_data(code=code, date=date).copy()
             df[EMVar.time] = date + ' ' + df[EMVar.time]
             df[EMVar.time] = pd.to_datetime(df[EMVar.time])
             df = df.set_index(df[EMVar.time])
             price_df = df[EMVar.price].resample(resample_baser_period).ohlc().dropna()
             volumns = df[EMVar.volume].resample(resample_baser_period).sum().dropna()
             vol_df = pd.DataFrame(volumns, columns=[EMVar.volume])

             amount = df[EMVar.amount].resample(resample_baser_period).sum().dropna()
             amount_df = pd.DataFrame(amount, columns=[EMVar.amount])
             new_df = price_df.merge(vol_df, left_index=True, right_index=True).merge(amount_df, left_index=True,
                                                                                      right_index=True)
             new_df[EMVar.code] = code
             new_df[EMVar.date] = date
             new_df[EMVar.time] = list(new_df.index)
             new_df['kType'] = resample_baser_period
             print('从Tushare获取%s在%s的%sK线' % (code, date,resample_baser_period))
        except Exception as e:
            if '当天没有数据' in df['time'][0]:
                insert_stop_trade_record(code=code, date=date, is_index=False)
                print('%s获取%sTick数据失败,当天没有数据'%(code, date))
            else:
                print('%s获取%sTick数据失败，SHAPE为%s'%(code, date, df.shape))
                print(e)

    else:
        print('从Mongo中获取%s在%s的1分钟K线' % (code, date))
        need_insert_db = False
        new_df = new_df.set_index(new_df[EMVar.time])
    if need_insert_db:
        for index, row in new_df.iterrows():
            mongoManager.db[mongoManager.COL_STK_MIN_HIS_DATA].update_one({EMVar.time: index,EMVar.code:dict(row)[EMVar.code]}, {'$set': row.to_dict()}, upsert=True)

    return new_df


def mongo_min_tick_data(code='', date='',kType='1min'):
    datas = mongoManager.db[mongoManager.COL_STK_MIN_HIS_DATA].find({EMVar.date: date, EMVar.code: code, 'kType': kType})
    if datas:
        return pd.DataFrame(data=list(datas))
    else:
        return pd.DataFrame()

def em_get_day_min_list():
    df = resample_tick_data(code='000970', date='2017-06-23',need_insert_db=False)
    df.drop(df.index[0], inplace=True)
    return [x.strftime('%H:%M:%S') for x in list(df.index)]

def em_get_total_trade_list():
    sh_his_data = ts.get_k_data(code=EMVar.CODE_SH, index=True, start='2001-01-01', end=datetime.datetime.now().strftime('%Y-%m-%d'))
    return list(sh_his_data[EMVar.date])


# 是否在交易时间
def is_in_trade_hour():
    try:
        now = datetime.datetime.now().strftime(EMVar.FORMATE_YMD_HMS)
        now_day = datetime.datetime.now().strftime(EMVar.FORMATE_YMD)
        morning_begin = now_day + ' 09:30:00'
        middle_stop = now_day + ' 11:30:00'
        afternoon_begin = now_day + ' 11:30:00'
        afternoon_stop = now_day + ' 15:00:00'
        t1 = time_utils.delta_date_str(now, morning_begin, formatter=EMVar.FORMATE_YMD_HMS)
        t2 = time_utils.delta_date_str(now, middle_stop, formatter=EMVar.FORMATE_YMD_HMS)
        t3 = time_utils.delta_date_str(now, afternoon_begin, formatter=EMVar.FORMATE_YMD_HMS)
        t4 = time_utils.delta_date_str(now, afternoon_stop, formatter=EMVar.FORMATE_YMD_HMS)
        if (t1[EMVar.SEC] > 0 and t2[EMVar.SEC] < 0) or (t3[EMVar.SEC] > 0 and t4[EMVar.SEC] < 0):
            return True
    except Exception as e:
        return False

def is_in_60min_refresh_range():
    try:
        now = datetime.datetime.now().strftime(EMVar.FORMATE_YMD_HMS)
        now_day = datetime.datetime.now().strftime(EMVar.FORMATE_YMD)
        open_hour = now_day + ' 09:30:00'
        first_hour = now_day + ' 10:30:00'
        second_hour = now_day + ' 11:30:00'
        third_hour = now_day + ' 14:00:00'
        last_hour = now_day + ' 15:00:00'
        t_ = time_utils.delta_date_str(now, open_hour, formatter=EMVar.FORMATE_YMD_HMS)
        t1 = time_utils.delta_date_str(now, first_hour, formatter=EMVar.FORMATE_YMD_HMS)
        t2 = time_utils.delta_date_str(now, second_hour, formatter=EMVar.FORMATE_YMD_HMS)
        t3 = time_utils.delta_date_str(now, third_hour, formatter=EMVar.FORMATE_YMD_HMS)
        t4 = time_utils.delta_date_str(now, last_hour, formatter=EMVar.FORMATE_YMD_HMS)
        if abs(t_[EMVar.MIN])<10 or abs(t1[EMVar.MIN])<10 or abs(t2[EMVar.MIN]) <10 \
                or abs(t3[EMVar.MIN]) <10 or abs(t4[EMVar.MIN]) <10:
            return True
    except Exception as e:
        print(e)
    return False

# resample_tick_data(code='600876', date='2017-08-31')

# em_get_day_min_list()

# is_in_trade_hour()

# is_in_60min_refresh_range()
